 /**
 * @author Clark
 * @email haixuanwoTxh@gmail.com
 * @date 2021-10-20
 */

#include <string.h>
#include "talk_protocol.h"

/**
 * @brief 以十六进制打印
 * @param data	数据
 * @param dataLen 数据长度
 */
void TalkProtocol::print_data_as_hex(unsigned char *data, int dataLen)
{
	char *buf = new char[dataLen*3 + 1]; // 一个十六进制用两个字节，加一个空格共3字节

    int i = 0;
    for(i = 0; i < dataLen; i++)
    {
        snprintf(buf + i*3, 4, "%02x ", data[i]); // snprintf会在数据后加'\0'，所以存4字节
    }

    std::cout<<"Data Len["<<dataLen<<"]:"<<buf<<std::endl;

    delete[] buf;
}

/**
 * @brief 设置数值到2字节空间
 * @param value 数值
 * @param buf 设置的地址
 */
void TalkProtocol::set_value_to_2Byte_buf(unsigned short value, unsigned char *buf)
{
	buf[1] = value&0xFF;
	buf[0] = value>>8 & 0xFF;
}

/**
 * @brief 获取2字节空间中的数值
 * @param  buf 获取的地址
 * @param  value 获取的数值
 */
void TalkProtocol::get_value_from_2Byte_buf(unsigned char *buf, unsigned short &value)
{
	value  = buf[0]*0x100 + buf[1];
}

/**
 * @brief 设置数值到4字节空间
 * @param value 设置的数值
 * @param buf 设置地址
 */
void TalkProtocol::set_value_to_4Byte_buf(unsigned int value, unsigned char *buf)
{
    buf[3] = value&0xFF;
    buf[2] = value>>8 & 0xFF;
    buf[1] = value>>16 & 0xFF;
    buf[0] = value>>24 & 0xFF;
}

/**
 * @brief 获取4字节空间中的数值
 * @param  buf 获取地址
 * @param  value 获取的数值
 */
void TalkProtocol::get_value_from_4Byte_buf(unsigned char *buf, unsigned int &value)
{
    value = buf[0]*0x1000000 + buf[1]*0x10000 + buf[2]*0x100 + buf[3];
}

/**
 * @brief 获取校验码，数据的字节累加和
 * @param data 		数据
 * @param dataLen	数据长度
 * @param checksum	校验码结果
 * @return true		成功
 * @return false	失败
 */
bool TalkProtocol::get_checksum(unsigned char *data, int dataLen, unsigned short &checksum)
{
	if (nullptr == data || dataLen <= 0)
	{
		printf("get checksum param error\n");
		return false;
	}

	checksum = 0;
	for (int i = 0; i < dataLen; i++)
	{
		checksum += data[i];
	}

	return true;
}

/**
 * @brief 构建协议包
 * @param action  	包行为类型
 * @param data		包数据
 * @param dataLen	数据长度
 * @param packet	存放数据包的缓存
 * @param packetBufSize	缓存空间大小
 * @param packetLen	包大小
 * @return true		成功
 * @return false	失败
 */
bool TalkProtocol::build_protocol_packet(Action action, unsigned char* data, int dataLen, unsigned char* packet, int packetBufSize, int &packetLen)
{
	int index = 0;
	unsigned short checksum = 0;

	if (nullptr == data || dataLen < 0 || nullptr == packet)
	{
		printf("build protocol packet para error\n");
		return false;
	}

	packetLen = dataLen + BASIC_PACKET_LEN;
	if (packetLen > packetBufSize)
	{
		printf("%s error packetLen > packetBufSize\n", __func__);
		return false;
	}

	// 包头
	set_value_to_2Byte_buf(HEAD, packet);
	index += 2;

	// 消息行为
	set_value_to_2Byte_buf(action, packet + index);
	index += 2;

	// 数据长度
	set_value_to_2Byte_buf(dataLen, packet + index);
	index += 2;

	// 数据
	memcpy(packet + index, data, dataLen);
	index += dataLen;

	// checksum
	set_value_to_2Byte_buf(0, packet + index);
	if (false == get_checksum(packet+2, index-2, checksum))
	{
		printf("build packet get checksum error\n");
		return false;
	}

	set_value_to_2Byte_buf(checksum, packet + index);
	index += 2;

	// 包尾
	set_value_to_2Byte_buf(END, packet + index);
	index += 2;

	if (index != packetLen)
	{
		printf("index[%d] packetLen[%d] error\n", index, packetLen);
		return false;
	}

	return true;
}

/**
 * @brief 解析协议包
 * @param packet	包内容
 * @param packetLen	包长度
 * @param action	解析出的包行为类型
 * @param data		解析出的数据
 * @param dataBufSize	存储数据缓存大小
 * @param dataLen	数据长度
 * @return true		成功
 * @return false	失败
 */
bool TalkProtocol::parse_protocol_packet(unsigned char* packet, int packetLen, Action &action, unsigned char* data, int dataBufSize, int &dataLen)
{
	if (nullptr == packet || packetLen < BASIC_PACKET_LEN || nullptr == data)
	{
		printf("parse packet param error\n");
		return false;
	}

	int index = 0;

	// 包头
	unsigned short head = 0;
	get_value_from_2Byte_buf(packet, head);
	if(HEAD != head)
	{
		printf("parse packet head error\n");
		return false;
	}
	index += 2;

	// 行为类型
	unsigned short getAction = 0;
	get_value_from_2Byte_buf(packet + index, getAction);
	action = (Action )getAction;
	index += 2;

	// 数据长度
	unsigned short len = 0;
	get_value_from_2Byte_buf(packet + index, len);
	dataLen = len;
	if (dataLen > packetLen - BASIC_PACKET_LEN)
	{
		printf("parse packet dataLen error\n");
		return false;
	}

	if (dataLen > dataBufSize)
	{
		printf("parse packet dataLen[%d] > dataBufSize[%d] error\n", dataLen, dataBufSize);
		return false;
	}
	index += 2;

	// 数据
	memcpy(data, packet + index, dataLen);
	index += dataLen;

	// 检验码
	unsigned short checksum = 0;
	get_value_from_2Byte_buf(packet + index, checksum);

	unsigned short checksumCount = 0;
	if (false == get_checksum(packet+2, index-2, checksumCount))
	{
		printf("parse packet get checksum error\n");
		return false;
	}

	if (checksum != checksumCount)
	{
		printf("parse packet checksum[%04x] checksumCount[%04x] error\n", checksum, checksumCount);
		return false;
	}
	index += 2;

	// 包尾
	unsigned short end = 0;
	get_value_from_2Byte_buf(packet+index, end);
	if (END != end)
	{
		printf("parse packet end error\n");
		return false;
	}
	index += 2;

	if (index != packetLen)
	{
		printf("parse packet index[%d] != packetLen[%d] error\n", index, packetLen);
		return false;
	}

	return true;
}

/**
 * @brief 从数据中获取用户名和密码
 * @param data		数据
 * @param dataLen	数据长度
 * @param user		用户名
 * @param passwd	密码
 * @return true		成功
 * @return false	失败
 */
bool TalkProtocol::get_name_passwd(unsigned char *data, int dataLen, std::string &user, std::string &passwd)
{
	int index = 0;
	unsigned short int userLen = 0;
	char userData[USR_MAX_LEN] = {0};
	unsigned short int passwdLen = 0;
	char passwdData[PASSWD_MAX_LEN] = {0};

	if (nullptr == data || dataLen < 6)
	{
		std::cout<<"data or dataLen error"<<std::endl;
		return false;
	}

	get_value_from_2Byte_buf(data, userLen);
	if (userLen > USR_MAX_LEN)
	{
		std::cout<<"user len["<<userLen<<"] error"<<std::endl;
	}
	index += 2;

	memcpy(userData, data + index, userLen);
	index += userLen;

	get_value_from_2Byte_buf(data + index, passwdLen);
	if (passwdLen > USR_MAX_LEN)
	{
		std::cout<<"passwd len["<<passwdLen<<"] error"<<std::endl;
	}
	index += 2;

	memcpy(passwdData, data + index, passwdLen);
	index += passwdLen;

	if (index != dataLen)
	{
		std::cout<<"dataLen["<<dataLen<<"] error"<<std::endl;
		return false;
	}

	user = userData;
	passwd = passwdData;

	return true;
}
